/* 
 * Copyright WestYao.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi/native_api.h"
#include "avcodec_log_adapter.h"
#include "sample_napi_wrapper.h"
#include "sample_helper.h"
#include <string>
#include <memory>

using namespace OHOS::MediaAVCodec::Sample;

namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_APP, LOG_DOMAIN_TEST, "NAPI_INIT"};
}

struct PlayAsyncData {
    uint64_t surfaceId;
    std::string uri;
    napi_deferred deferred;
};

static void ExecutePlay(napi_env env, void* data)
{
    auto asyncData = static_cast<PlayAsyncData*>(data);
    if (!asyncData) {
        AVCODEC_LOGE("ExecutePlay: asyncData is null");
        return;
    }

    bool isUri = (asyncData->uri.find("http") != std::string::npos) ? true : false;
    SampleInfo sampleInfo = {
        .inputFilePath = asyncData->uri.c_str(),
        .dataProducerInfo = {
            .demuxerSourceType = isUri ? DEMUXER_SOURCE_TYPE_URI : DEMUXER_SOURCE_TYPE_FILE,
        },
        .codecConsumerType = CODEC_CONSUMER_TYPE_DECODER_RENDER_OUTPUT,
        .surfaceId = asyncData->surfaceId
    };

    RunSample(sampleInfo);
}

static void CompletePlay(napi_env env, napi_status status, void* data)
{
    auto asyncData = static_cast<PlayAsyncData*>(data);
    if (!asyncData) {
        AVCODEC_LOGE("CompletePlay: asyncData is null");
        return;
    }

    napi_value result;
    napi_create_string_utf8(env, "Play completed", NAPI_AUTO_LENGTH, &result);
    napi_resolve_deferred(env, asyncData->deferred, result);
    delete asyncData;
}

static napi_value Play(napi_env env, napi_callback_info info)
{
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");

    size_t argc = 2;
    napi_value args[2] = {nullptr};
    auto ret = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    CHECK_AND_RETURN_RET_LOG(ret == napi_ok, nullptr, "napi_get_cb_info failed");

    // 获取 surfaceId
    uint64_t surfaceId = 0LL;
    bool lossless = true;
    ret = napi_get_value_bigint_uint64(env, args[0], &surfaceId, &lossless);
    CHECK_AND_RETURN_RET_LOG(ret == napi_ok, nullptr, "Get surface id failed");

    // 获取 URI
    size_t uriLength = 0;
    ret = napi_get_value_string_utf8(env, args[1], nullptr, 0, &uriLength);
    CHECK_AND_RETURN_RET_LOG(ret == napi_ok, nullptr, "Get URI length failed");

    std::unique_ptr<char[]> uri(new char[uriLength + 1]);
    ret = napi_get_value_string_utf8(env, args[1], uri.get(), uriLength + 1, &uriLength);
    CHECK_AND_RETURN_RET_LOG(ret == napi_ok, nullptr, "Get URI failed");

    // 创建异步任务
    napi_value promise;
    auto asyncData = new PlayAsyncData{surfaceId, uri.get(), nullptr};
    napi_create_promise(env, &asyncData->deferred, &promise);
    
    napi_value work_name;
    napi_create_string_utf8(env, "PlayAsyncWork", NAPI_AUTO_LENGTH, &work_name);
    napi_async_work asyncWork;
    ret = napi_create_async_work(env, nullptr, work_name, ExecutePlay, CompletePlay, asyncData, &asyncWork);
    if (ret != napi_ok) {
        AVCODEC_LOGE("Failed to create async work, error code: %{public}d", ret);
        delete asyncData;
        return nullptr;
    }

    ret = napi_queue_async_work(env, asyncWork);
    if (ret != napi_ok) {
        AVCODEC_LOGE("Failed to queue async work");
        napi_delete_async_work(env, asyncWork);
        delete asyncData;
        return nullptr;
    }

    return promise;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    CHECK_AND_RETURN_RET_LOG(env != nullptr && exports != nullptr, nullptr, "env or exports is null");

    napi_property_descriptor desc[] = {
        { "Play", nullptr, Play, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);

    SampleNapiWrapper::Export(env, exports);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "avcodecdemo",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterAVCodecDemoModule(void)
{
    napi_module_register(&demoModule);
}
